module horizon_projection(
	//global clock
	input				clk,  				//cmos video pixel clock
	input				rst_n,				//global reset

	//Image data prepred to be processd
	input				per_frame_vsync,	//Prepared Image data vsync valid signal
	input				per_frame_href ,		//Prepared Image data href vaild  signal
	input				per_frame_clken,	//Prepared Image data output/capture enable clock
	input				per_img_Bit,		//Prepared Image Bit flag outout(1: Value, 0:inValid)
	
	//Image data has been processd
	output				post_frame_vsync,	//Processed Image data vsync valid signal
	output				post_frame_href,	//Processed Image data href vaild  signal
	output				post_frame_clken,	//Processed Image data output/capture enable clock
	output   		    post_img_Bit, 		//Processed Image Bit flag outout(1: Value, 0:inValid)
	
    input      [9:0] 	prj_x_start,		//投影起始列
    input      [9:0] 	prj_x_end  ,		//投影结束列 
    input      [9:0] 	prj_y_start,		//投影起始行
    input      [9:0] 	prj_y_end  , 		//投影结束行  

    output reg [9:0] 	hor_up      ,         //边沿坐标
    output reg [9:0] 	hor_down    ,
    output reg [9:0] 	ver_left    ,
    output reg [9:0] 	ver_right   ,   

    output     [9:0]    obj_x           ,       //物块坐标
    output     [9:0]    obj_y           ,

    output reg [19:0]   sum_pixel           //薄片像素点之和
);

parameter	IMG_HDISP = 10'd1023;	//1024*600
parameter	IMG_VDISP = 10'd599;

reg         per_img_Bit_r;
reg         post_img_Bit_r;

wire [9:0] x_pos;
wire [9:0] y_pos;

assign post_img_Bit = per_img_Bit_r;

assign obj_x = ver_left + ((ver_right-ver_left)>>1);
assign obj_y = hor_up + ((hor_down-hor_up)>>1);

coordinate_gen  u_coordinate_gen (
    .clk                     (clk            ),
    .rst_n                   (rst_n          ),
    .per_frame_vsync         (per_frame_vsync),
    .per_frame_href          (per_frame_href ),
    .per_frame_valid         (per_frame_clken),
 
    .post_frame_vsync        (post_frame_vsync),
    .post_frame_href         (post_frame_href ),
    .post_frame_valid        (post_frame_clken),

    .pixel_xpos              ( x_pos),
    .pixel_ypos              ( y_pos)
);

//lag 1 clock
reg post_frame_vsync_r;
reg post_frame_href_r;

always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)begin
		post_frame_vsync_r 	<= 1'd0;
        post_frame_href_r <= 1'd0;
		end
	else begin
		post_frame_vsync_r 	<= 	post_frame_vsync;
        post_frame_href_r <= post_frame_href;
		end
end

wire vsync_pos_flag;
assign vsync_pos_flag = post_frame_vsync & (~post_frame_vsync_r);

wire href_neg_flag;
assign href_neg_flag = ~post_frame_href & post_frame_href_r;

//lag 1 clock
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        per_img_Bit_r <= 1'd0;
    end
    else begin
        per_img_Bit_r <= per_img_Bit;
    end
end

//lag 1 clock
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        post_img_Bit_r <= 1'd0;
    end
    else begin
        post_img_Bit_r <= post_img_Bit;
    end
end

//------------------------------------------
//寄存“行/场”方向计数
reg [9:0]  	x_pos_r;
reg [9:0]   y_pos_r;

always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			x_pos_r <= 10'd0;
			y_pos_r <= 10'd0;
		end
	else begin
			x_pos_r <= x_pos;
            y_pos_r <= y_pos;
		end
end

//------------------------------------------
//水平方向投影
reg  		ram_wr;
wire [9:0] 	ram_wr_data;
wire [9:0] 	ram_rd_data;

always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        ram_wr <= 1'b0;
    end
    else if(post_frame_clken && (x_pos < IMG_HDISP) && (y_pos != IMG_VDISP))
        ram_wr <= 1'b1;
    else
        ram_wr <= 1'b0;
end

wire [9:0] ram_wr_addr;
wire [9:0] ram_rd_addr;

//IP 核RAM（下板用)
ram_horizon u_ram_horizon(
	.clock      (clk),
	.data       (ram_wr_data),
	.rdaddress  (ram_rd_addr),
	.wraddress  (ram_wr_addr),
	.wren       (ram_wr),
	.q          (ram_rd_data)
);

//手写RAM（仿真用）
// myram #(
//     .WIDTH  ( 10  ),
//     .DEPTH  ( 1024  ),
//     .DEPBIT ( 10 )
// )
// u_myram (
//     .clk    ( clk         ),
//     .we     ( ram_wr      ),
//     .waddr  ( ram_wr_addr ),
//     .raddr  ( ram_rd_addr ),
//     .dq_i   ( ram_wr_data ),

//     .dq_o   ( ram_rd_data )
// );
	
reg [9:0] rd_data_d1;

always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        rd_data_d1 <= 10'd0;
    end
    else if(post_frame_clken) begin
        rd_data_d1 <= ram_rd_data;
	end
    else begin
        rd_data_d1 <= rd_data_d1;
    end
end

reg [9:0]   ver_left_reg;
reg [9:0]   ver_right_reg;
reg [9:0]   ver_min;//垂直边界坐标坐标
reg [9:0]   ver_max;
reg [9:0]   y_reg;//寄存纵坐标
reg [1:0]   prj_flag;	//投影标志信号
reg [19:0]  sum_pixel_cnt;
reg [9:0]   rise_cnt;//检测到的上升沿个数（避免平行情况的锯齿造成ver_min抖动)
wire        pixel_pos;//像素点上升沿（白 to 黑）
wire        pixel_neg;//像素点下降沿（黑 to 白）

assign pixel_pos = (post_img_Bit==1'd1) && (post_img_Bit_r==1'd0);
assign pixel_neg = (post_img_Bit==1'd0) && (post_img_Bit_r==1'd1);
// assign pixel_pos = (post_img_Bit_r[4:0] == 5'b1_1111) && (post_img_Bit_r[9:5] != 5'b1_1111);
// assign pixel_neg = (post_img_Bit_r[4:0] != 5'b1_1111) && (post_img_Bit_r[9:5] == 5'b1_1111);

parameter x_thresh = 10'd30;
parameter y_thresh = 10'd4;
// parameter x_thresh = 10'd30;
// parameter y_thresh = 10'd2;

//--------------------------------------------------------------------------------------------------------------------------------------
//薄片边缘检测，生成投影有效信号，获得薄片左右边界
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        ver_min <= IMG_HDISP;
        ver_max <= 1'd0;
        prj_flag <= 1'd0;
        y_reg <= 1'd0;
        ver_left_reg <= 1'd0;
        ver_right_reg <= 1'd0;
        sum_pixel_cnt <= 1'd0;
        sum_pixel <= 1'd0;
        rise_cnt <= 1'd0;
    end
    else if(vsync_pos_flag)begin // 进入了一个新的行
        ver_min <= IMG_HDISP;
        ver_max <= 1'd0;
        y_reg <= 1'd0;
        ver_left_reg <= 1'd0;
        ver_right_reg <= 1'd0;
        sum_pixel_cnt <= 1'd0;
        sum_pixel <= sum_pixel_cnt;
        prj_flag <= prj_flag;
        rise_cnt <= 1'd0;
    end
    else if(href_neg_flag)begin // 检测完一行
        ver_min <= ver_min;//可以删
        ver_max <= ver_max;
        ver_left_reg <= ver_left_reg;
        ver_right_reg <= ver_right_reg;
        prj_flag <= 1'd0; // 清零
        rise_cnt <= rise_cnt;
    end
    else if(post_frame_clken) begin // 一个像素来临
        //在设定范围内进行寻找
        if((x_pos_r > prj_x_start) && (x_pos_r < prj_x_end) && 
            (y_pos_r > prj_y_start) && (y_pos_r < prj_y_end))begin // y没有意义
            //找到对应行的上升沿
            if(pixel_pos && (prj_flag == 2'd0))begin
                //有效行数计数器大于8，避免锯齿状
                if(rise_cnt > 10'd8)begin
                    if(ver_left_reg == 1'd0)begin//寄存第一个点
                        ver_left_reg <= x_pos_r;
                        ver_min <= ver_min;//可以删
                        y_reg <= y_pos_r;
                        prj_flag <= prj_flag;
                    end
                    else begin
                        //在前一个目标范围内
                        if(((ver_left_reg - x_pos_r < x_thresh) || (x_pos_r - ver_left_reg < x_thresh)) &&
                                ((y_reg - y_pos_r < y_thresh) || (y_pos_r - y_reg < y_thresh))) begin
                            prj_flag <= 2'd1; // 进入二阶段
                            ver_left_reg <= x_pos_r;
                            y_reg <= y_pos_r;

                            if(ver_min > ver_left_reg)begin //寻找最左端点
                                ver_min <= ver_left_reg;
                            end
                            else begin
                                ver_min <= ver_min;//可以删
                            end

                        end
                        else begin
                            ver_left_reg <= ver_left_reg;
                            prj_flag <= prj_flag;
                        end
                    end
                end
                else begin
                    rise_cnt <= rise_cnt + 1'd1;
                    ver_min <= ver_min;
                    ver_left_reg <= ver_left_reg;
                    prj_flag <= prj_flag;
                end
            end
            else if(pixel_neg && (prj_flag == 1'd1))begin //找到对应行的下降沿
                if(ver_max < ver_right_reg)begin //寻找最右端点
                    ver_max <= ver_right_reg;
                end
                prj_flag <= 2'd2; // 进入三阶段
                ver_right_reg <= x_pos_r;
                sum_pixel_cnt <= sum_pixel_cnt + (x_pos_r - ver_left_reg); // 面积
            end
            else begin
                ver_min <= ver_min;//可以删
                ver_left_reg <= ver_left_reg;
                prj_flag <= prj_flag;
            end
        end
        else begin
            ver_min <= ver_min;//可以删
            ver_left_reg <= ver_left_reg;
        end
        
	end
    else begin
        ver_min <= ver_min;//可以删
        ver_left_reg <= ver_left_reg;
    end
end

reg [9:0]   hor_min;//水平中值坐标
reg [9:0]   hor_max;
reg [2:0]   hor_min_cnt;
reg [2:0]   hor_max_cnt;
reg [1:0]   rise_flag; //上升沿标志

parameter med_thresh = 50;//中值阈值

//--------------------------------------------------------------------------------------------------------------------------------------
//图像的倒数第二行，寻找投影上升、下降沿中值坐标，锁定上边界范围
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        hor_min <= 1'd0;
        hor_max <= 1'd0;
        hor_min_cnt <= 1'd0;
        hor_max_cnt <= 1'd0;
		rise_flag	<= 1'b0;
    end
    else if(vsync_pos_flag)begin
        hor_min <= 1'd0;
        hor_max <= 1'd0;
        hor_min_cnt <= 1'd0;
        hor_max_cnt <= 1'd0;
		rise_flag	<= 1'b0;
    end
    else if(post_frame_clken) begin

        //图像的倒数第二行，遍历RAM中的数据,寻找中值坐标
        if(y_pos == IMG_VDISP - 2'd1) begin    
		
			//此处表示上边界
			if((rise_flag == 1'b0) && (ram_rd_data >= med_thresh) && (rd_data_d1 <= med_thresh)) begin	//第一个上升沿

                rise_flag <= 1'b1;
                hor_min_cnt <= hor_min_cnt + 1'd1;

                case (hor_min_cnt)
                    3'd0: hor_min <= x_pos_r;
                    default:begin
                        hor_min <= hor_min;
                    end
                endcase

			end	
			
			//此处表示下边界
			if((rise_flag == 1'b1) && (ram_rd_data <= med_thresh) && (rd_data_d1 >= med_thresh)) begin	//下降沿不断迭代，直到最后一个下降沿

                rise_flag <= 1'b0;
                hor_max_cnt <= hor_max_cnt + 1'd1;

                case (hor_max_cnt)
                    3'd0: hor_max <= x_pos_r; 
                    default:begin
                        hor_max <= hor_max;
                    end
                endcase

			end	
    
		end
		
	end
end

reg [9:0] 	hor_up_reg;//边界坐标
reg [9:0] 	hor_down_reg;

reg [9:0]   hor_up_minnum;
reg [9:0]   hor_down_minnum;

//--------------------------------------------------------------------------------------------------------------------------------------
//图像的倒数第一行，寻找中值坐标间的最小值
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        hor_up_reg <= 1'd0;
        hor_down_reg <= 1'd0;
        hor_up_minnum <= IMG_HDISP;//赋值一行的最大点数，方便后面比较
        hor_down_minnum <= IMG_HDISP;
    end
    else if(vsync_pos_flag)begin
        hor_up_minnum <= IMG_HDISP;
        hor_down_minnum <= IMG_HDISP;
    end
    else if(post_frame_clken) begin

        if(y_pos == IMG_VDISP) begin    //图像的倒数第一行，遍历RAM中的数据,寻找最小坐标
		
			//当前坐标在范围内，且当前坐标的值小于寄存的值
            if((x_pos >= 10'd5) && (x_pos < hor_min) && (ram_rd_data <= hor_up_minnum)) begin	
                hor_up_reg <= x_pos;//重新赋值坐标
                hor_up_minnum <= ram_rd_data;//重新赋值最小数据
			end
            else if((x_pos >= hor_max) && (x_pos <= IMG_VDISP - 10'd4) && (ram_rd_data < hor_down_minnum)) begin	
                hor_down_reg <= x_pos;//重新赋值坐标
                hor_down_minnum <= ram_rd_data;//重新赋值最小数据
			end	
    
		end

	end
end

//--------------------------------------------------------------------------------------------------------------------------------------
//帧信号上升沿更新水平线坐标
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        hor_up    <= 1'd0;
        hor_down  <= 1'd0;
        ver_left  <= 1'd0;
        ver_right <= 1'd0;
    end
    else if(vsync_pos_flag) begin
		hor_up    <= hor_up_reg - 10'd8;
		hor_down  <= hor_down_reg;
        ver_left  <= ver_min;
        ver_right <= ver_max;
    end  
    else begin
        hor_up    <= hor_up   ;
        hor_down  <= hor_down ;
        ver_left  <= ver_left ;
        ver_right <= ver_right;
    end 
end

assign ram_wr_data = (y_pos == 1'd0) ? 10'd0 : 				//图像的第一行，RAM清零
                        (prj_flag == 2'd1) ? (ram_rd_data + post_img_Bit_r) :
                            ram_rd_data;

//在图像人的第一行和最后一行，需要遍历RAM中的数据
assign ram_wr_addr = (y_pos == 1'd0)  ?  x_pos : y_pos_r;


//在图像人的第一行和最后一行，需要遍历RAM中的数据
assign ram_rd_addr = ((y_pos == 1'd0) || (y_pos == IMG_VDISP) || (y_pos == IMG_VDISP - 2'd1))  ?  x_pos : y_pos;


endmodule
